Bienvenidos a la Actividad 1, donde pondremos en práctica todo lo aprendido durante el bloque 2. Esta actividad la realizaremos en clase, se terminará en casa (debería completarse en clase) y se entregará el día 8 de octubre.
Vamos a poner en práctica cuatro aspectos del procesamiento de imágenes:
La finalidad es sencilla. Se os dará una imagen, a color, que tiene varias tonalidades y que está pintada con círculos.
La actividad consiste en contar el número de círculos de la imagen.
Se evaluará de la siguiente manera:
No se aceptará el formato .ipynb Habilitaré una actividad en Canvas para que podáis subir ambos archivos.
En primer lugar, cargamos todos los paquetes/frameworks que nos van a hacer falta. Se recomienda visitar la web: https://scikit-image.org/ para ver todas las funcionalidades que permite Scikit Image.
# Paquetes necesarios para la realización de esta práctica (no son necesarios conocerlos ni entenderlos por ahora)
from skimage.io import imread
from skimage import transform as tf
import matplotlib.pyplot as plt
# Cargamos la función para convertir de RGB a Escala de grises
from skimage.color import rgb2gray
# Paquete y funciones para realizar una umbralización con Scikit-image
from skimage.filters import threshold_otsu, threshold_local, threshold_niblack, threshold_sauvola
# Paquetes necesarios para la morfología matemática
from skimage.morphology import erosion, dilation, opening, closing
# Elementos estructurales
from skimage.morphology import disk, diamond, ball, rectangle
# Estas dos funciones nos sirven para detectar los objetos dentro de una imagen binaria
from skimage.morphology import label
from skimage.measure import regionprops
# Defino una función para mostrar una imagen por pantalla con el criterio que considero más acertado
def imshow(img, title):
fig, ax = plt.subplots(figsize=(7, 7))
# El comando que realmente muestra la imagen
ax.imshow(img,cmap=plt.cm.gray)
# Para evitar que aparezcan los números en los ejes
ax.set_xticks([]), ax.set_yticks([])
ax.set_title(title)
plt.show()
Lo primero de todo, vamos a leer la imagen. Recuerda que hay que subir la imagen cada vez que inicies sesión en el notebook y que la ruta se mira haciendo botón derecho sobre el archivo.
Con lo cual, aquí vamos a hacer dos cosas:
Hacemos esto para luego posteriormente umbralizar la imagen en escala de grises.
pip install pillow
Requirement already satisfied: pillow in c:\users\rodri\anaconda3\lib\site-packages (10.0.1)Note: you may need to restart the kernel to use updated packages.
from PIL import Image
from IPython.display import display
# Ruta de la imagen que deseas importar
ruta_imagen = 'Pintura_Puntos.jpg'
# Abre la imagen utilizando Pillow
imagen = Image.open(ruta_imagen)
# Muestra la imagen en el Jupyter Notebook
display(imagen)
# Convierte la imagen a escala de grises
imagen_gris = imagen.convert('L')
# Guarda la imagen en escala de grises si es necesario
# imagen_gris.save('imagen_gris.png')
# Muestra la imagen en escala de grises
display(imagen_gris)
Vamos a probar ahora diferentes métodos para umbralizar la imagen. Se pide en esta actividad:
from PIL import Image, ImageOps
ruta_imagen = 'Pintura_Puntos.jpg'
imagen = Image.open(ruta_imagen)
umbral = 75 # Umbral (valor entre 0 y 255)
imagen_binaria = ImageOps.grayscale(imagen) #pasamos a escala de grises
imagen_binaria = imagen_binaria.point(lambda p: p > umbral and 255)
display(imagen_binaria)
Podemos modificar el umbral. Si aumentas el valor del umbral se volverá más restrictivo y solo los píxeles con valores de intensidad mucho más altos que 100 serán considerados blancos. Esto hara que haya regiones más oscuras. Si disminuyes el valor del umbral se volverá menos restrictivo y más píxeles tendrán intensidad suficiente para ser considerados blancos. Se verán zonas más claras.
from PIL import Image, ImageOps
ruta_imagen = 'Pintura_Puntos.jpg'
imagen = Image.open(ruta_imagen)
umbral = 50 # Umbral (valor entre 0 y 255)
imagen_binaria = ImageOps.grayscale(imagen) #pasamos a escala de grises
imagen_binaria = imagen_binaria.point(lambda p: p > umbral and 255)
display(imagen_binaria)
Con un valor de umbral tan bajo, los puntos casi no se distinguen en las reguiones más claras de la imagen.
from PIL import Image, ImageOps
ruta_imagen = 'Pintura_Puntos.jpg'
imagen = Image.open(ruta_imagen)
umbral = 130 # Umbral (valor entre 0 y 255)
imagen_binaria = ImageOps.grayscale(imagen) #pasamos a escala de grises
imagen_binaria = imagen_binaria.point(lambda p: p > umbral and 255)
display(imagen_binaria)
Con un valor de umbral muy alto, en las zonas claras los puntos se distinguen muy bien pero en las zonas ocuras apenas podemos diferenciar puntos.
pip install scikit-image
Requirement already satisfied: scikit-image in c:\users\rodri\anaconda3\lib\site-packages (0.21.0) Requirement already satisfied: numpy>=1.21.1 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (1.24.4) Requirement already satisfied: pillow>=9.0.1 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (10.0.1) Requirement already satisfied: imageio>=2.27 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (2.31.5) Requirement already satisfied: tifffile>=2022.8.12 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (2023.7.10) Requirement already satisfied: scipy>=1.8 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (1.10.1) Requirement already satisfied: networkx>=2.8 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (3.1) Requirement already satisfied: lazy_loader>=0.2 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (0.3) Requirement already satisfied: PyWavelets>=1.1.1 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (1.1.1) Requirement already satisfied: packaging>=21 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (23.2) Note: you may need to restart the kernel to use updated packages.
from skimage import io, color, filters
import matplotlib.pyplot as plt
# Carga la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convierte la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Calcula el umbral de Otsu
umbral = filters.threshold_otsu(imagen_gris)
# Aplica el umbral de Otsu
imagen_umbralizada = imagen_gris > umbral
# Muestra la imagen umbralizada
plt.imshow(imagen_umbralizada, cmap='gray')
plt.axis('off')
plt.show()
from skimage import io, color, filters
import matplotlib.pyplot as plt
# Carga la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convierte la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Calcula el umbral de Li
umbral = filters.threshold_li(imagen_gris)
# Aplica el umbral de Li
imagen_umbralizada = imagen_gris > umbral
# Muestra la imagen umbralizada
plt.imshow(imagen_umbralizada, cmap='gray')
plt.axis('off')
plt.show()
from skimage import io, color, filters
import matplotlib.pyplot as plt
# Carga la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convierte la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Aplica umbralización simple
umbral = 0.27 # Puedes ajustar el valor del umbral
imagen_umbralizada = imagen_gris > umbral
# Muestra la imagen umbralizada
plt.imshow(imagen_umbralizada, cmap='gray')
plt.axis('off')
plt.show()
from skimage import io, color, filters
import matplotlib.pyplot as plt
# Carga la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convierte la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Define el umbral personalizado (ajusta este valor según tus necesidades)
umbral_personalizado = 0.22
# Aplica la umbralización de Li con el umbral personalizado
imagen_umbralizada = imagen_gris > umbral_personalizado
# Muestra la imagen umbralizada
plt.imshow(imagen_umbralizada, cmap='gray')
plt.axis('off')
plt.show()
Métodos threshold_niblack y threshold_sauvola: son algoritmos utilizados en el procesamiento de imágenes para la umbralización local. Estos métodos no aplican un umbral único a toda la imagen; en cambio, calculan un umbral diferente para cada píxel en función de su entorno local. Esto es útil en situaciones en las que la iluminación de la imagen es inconsistente o variable.
from skimage import io, color, filters
import matplotlib.pyplot as plt
# Carga la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convierte la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Aplica umbralización con threshold_sauvola
umbralizado_sauvola = filters.threshold_sauvola(imagen_gris, window_size=25, k=0.2)
# Aplica el umbral
imagen_umbralizada_sauvola = imagen_gris > umbralizado_sauvola
# Muestra la imagen umbralizada con threshold_sauvola
plt.imshow(imagen_umbralizada_sauvola, cmap='gray')
plt.axis('off')
plt.show()
from skimage import io, color, filters
import matplotlib.pyplot as plt
# Carga la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convierte la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Aplica umbralización con threshold_niblack
umbralizado_niblack = filters.threshold_niblack(imagen_gris, window_size=15, k=0.4)
# Aplica el umbral
imagen_umbralizada_niblack = imagen_gris > umbralizado_niblack
# Muestra la imagen umbralizada con threshold_niblack
plt.imshow(imagen_umbralizada_niblack, cmap='gray')
plt.axis('off')
plt.show()
Vamos a probar a rotar la imagen
import numpy as np
from skimage import io, color, transform, filters
# Cargar la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convertir la imagen a escala de grises si es necesario
if len(imagen.shape) == 3:
imagen_gris = color.rgb2gray(imagen)
else:
imagen_gris = imagen
# Aplica umbralización con threshold_niblack
umbralizado_niblack = filters.threshold_niblack(imagen_gris, window_size=31, k=0.2)
# Aplica el umbral
imagen_umbralizada_niblack = imagen_gris > umbralizado_niblack
# Rotar la imagen umbralizada 180 grados
imagen_rotada = transform.rotate(imagen_umbralizada_niblack, 180)
# Mostrar la imagen rotada umbralizada
io.imshow(imagen_rotada, cmap='gray')
io.show()
Si se rota una imagen 180 grados antes de umbralizarla, no siempre se obtendrá el mismo resultado. El resultado puede diferir según la imagen y el algoritmo de umbralización utilizado. Esto se debe a cómo funcionan los algoritmos de umbralización y cómo la rotación de la imagen puede afectarlos. Un factor que puede afectar es la orientación de los objetos. Si los obajetos rotan es posible que los algoritmos de umbralización identifiquen de manera diferente las regiones de interés.
La umbralización es un proceso crucial en el procesamiento de imágenes que transforma una imagen en escala de grises en una imagen binaria, con dos grupos de píxeles, uno por encima y otro por debajo del umbral. Para lograr esto, hay una variedad de métodos de umbralización disponibles, cada uno con sus propias características y usos. Aquí hay una comparación general de cinco métodos de umbralización populares: umbralización binaria simple, umbralización de Otsu, umbralización de Li, umbralización de Sauvola y umbralización de Niblack.
Umbralización binaria simple:
Descripción: Este método utiliza un umbral fijo predefinido para separar los píxeles en dos grupos: por encima del umbral y por debajo del umbral. Resultados: Es simple pero no se adapta automáticamente a las variaciones de contraste y luminosidad en la imagen. Funciona bien cuando las condiciones de iluminación son uniformes. Aplicaciones: Útil en situaciones en las que el umbral es conocido y constante. Umbralización de Otsu:
Descripción: Calcula el umbral óptimo de manera automática al minimizar la varianza intraclase, buscando un valor de umbral que maximice la separación entre las clases de píxeles. Resultados: Funciona bien cuando la imagen tiene bimodalidad en su histograma de intensidad. Aplicaciones: Ampliamente utilizado en la segmentación de imágenes médicas y en aplicaciones de reconocimiento de objetos. Umbralización de Li:
Descripción: Calcula el umbral óptimo de manera automática utilizando una combinación de estadísticas locales y globales. Resultados: Funciona bien en imágenes con variaciones locales y contrastes diferentes. Aplicaciones: Útil en imágenes con variaciones de iluminación y sombras. Método Sauvola:
Descripción: Calcula el umbral local en ventanas pequeñas y adapta el umbral en función de la media y la desviación estándar locales. Resultados: Funciona bien en imágenes con variaciones locales de iluminación y contraste. Aplicaciones: Útil en OCR (reconocimiento óptico de caracteres) y procesamiento de documentos. Método Niblack:
Descripción: Similar al método Sauvola, calcula el umbral local en ventanas pequeñas utilizando la media y la desviación estándar locales. Resultados: Funciona bien en imágenes con variaciones locales de iluminación y contraste. Aplicaciones: Utilizado en aplicaciones de segmentación de texto en documentos y OCR.
El tipo de imagen y las circunstancias del problema determinan el método de umbralización. En general, los métodos de umbralización adaptativa, como Sauvola y Niblack, suelen funcionar mejor en imágenes con variaciones locales de iluminación, mientras que métodos como Otsu y Li son útiles cuando se requiere un umbral global ideal. Es crucial probar y ajustar estos métodos en función del tipo de imagen y el objetivo de procesamiento.
Como se puede apreciar en la imagen hay varios elementos imperfectos:
Mediante el uso de morfología matemática (concretamente los cuatro operadores visto en clase) y los posibles elementos estructurales existentes, se pide:
pip install opencv-python
Requirement already satisfied: opencv-python in c:\users\rodri\anaconda3\lib\site-packages (4.8.1.78) Requirement already satisfied: numpy>=1.17.0; python_version >= "3.7" in c:\users\rodri\anaconda3\lib\site-packages (from opencv-python) (1.24.4) Note: you may need to restart the kernel to use updated packages.
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Cargar la imagen en escala de grises
imagen = cv2.imread('Pintura_Puntos.jpg', cv2.IMREAD_GRAYSCALE)
# Definir el elemento estructural (kernel)
kernel = np.ones((5, 5), np.uint8) # Puedes ajustar el tamaño del kernel según tus necesidades
# Erosión
erosion = cv2.erode(imagen, kernel, iterations=1)
# Dilatación
dilatacion = cv2.dilate(imagen, kernel, iterations=1)
# Apertura (erosión seguida de dilatación)
apertura = cv2.morphologyEx(imagen, cv2.MORPH_OPEN, kernel)
# Cierre (dilatación seguida de erosión)
cierre = cv2.morphologyEx(imagen, cv2.MORPH_CLOSE, kernel)
# Mostrar las imágenes resultantes
plt.figure(figsize=(12, 6))
plt.subplot(2, 3, 1)
plt.imshow(imagen, cmap='gray')
plt.title('Imagen Original')
plt.axis('off')
plt.subplot(2, 3, 2)
plt.imshow(erosion, cmap='gray')
plt.title('Erosión')
plt.axis('off')
plt.subplot(2, 3, 3)
plt.imshow(dilatacion, cmap='gray')
plt.title('Dilatación')
plt.axis('off')
plt.subplot(2, 3, 4)
plt.imshow(apertura, cmap='gray')
plt.title('Apertura')
plt.axis('off')
plt.subplot(2, 3, 5)
plt.imshow(cierre, cmap='gray')
plt.title('Cierre')
plt.axis('off')
plt.tight_layout()
plt.show()
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Cargar la imagen en escala de grises
imagen = cv2.imread('Pintura_Puntos.jpg', cv2.IMREAD_GRAYSCALE)
# Definir diferentes elementos estructurales
elemento_disk = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
elemento_diamond = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
elemento_ball = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
elemento_rectangle = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 10)) # Cambia el tamaño según sea necesario
# Aplicar erosión con diferentes elementos estructurales
erosion_disk = cv2.erode(imagen, elemento_disk, iterations=1)
erosion_diamond = cv2.erode(imagen, elemento_diamond, iterations=1)
erosion_ball = cv2.erode(imagen, elemento_ball, iterations=1)
erosion_rectangle = cv2.erode(imagen, elemento_rectangle, iterations=1)
# Aplicar dilatación con diferentes elementos estructurales
dilatacion_disk = cv2.dilate(imagen, elemento_disk, iterations=1)
dilatacion_diamond = cv2.dilate(imagen, elemento_diamond, iterations=1)
dilatacion_ball = cv2.dilate(imagen, elemento_ball, iterations=1)
dilatacion_rectangle = cv2.dilate(imagen, elemento_rectangle, iterations=1)
# Aplicar apertura con diferentes elementos estructurales
apertura_disk = cv2.morphologyEx(imagen, cv2.MORPH_OPEN, elemento_disk)
apertura_diamond = cv2.morphologyEx(imagen, cv2.MORPH_OPEN, elemento_diamond)
apertura_ball = cv2.morphologyEx(imagen, cv2.MORPH_OPEN, elemento_ball)
apertura_rectangle = cv2.morphologyEx(imagen, cv2.MORPH_OPEN, elemento_rectangle)
# Aplicar cierre con diferentes elementos estructurales
cierre_disk = cv2.morphologyEx(imagen, cv2.MORPH_CLOSE, elemento_disk)
cierre_diamond = cv2.morphologyEx(imagen, cv2.MORPH_CLOSE, elemento_diamond)
cierre_ball = cv2.morphologyEx(imagen, cv2.MORPH_CLOSE, elemento_ball)
cierre_rectangle = cv2.morphologyEx(imagen, cv2.MORPH_CLOSE, elemento_rectangle)
# Mostrar las imágenes resultantes
plt.figure(figsize=(12, 14))
plt.subplot(4, 5, 1)
plt.imshow(imagen, cmap='gray')
plt.title('Imagen Original')
plt.axis('off')
plt.subplot(4, 5, 2)
plt.imshow(erosion_disk, cmap='gray')
plt.title('Erosión (Disk)')
plt.axis('off')
plt.subplot(4, 5, 3)
plt.imshow(erosion_diamond, cmap='gray')
plt.title('Erosión (Diamond)')
plt.axis('off')
plt.subplot(4, 5, 4)
plt.imshow(erosion_ball, cmap='gray')
plt.title('Erosión (Ball)')
plt.axis('off')
plt.subplot(4, 5, 5)
plt.imshow(erosion_rectangle, cmap='gray')
plt.title('Erosión (Rectangle)')
plt.axis('off')
plt.subplot(4, 5, 6)
plt.imshow(dilatacion_disk, cmap='gray')
plt.title('Dilatación (Disk)')
plt.axis('off')
plt.subplot(4, 5, 7)
plt.imshow(dilatacion_diamond, cmap='gray')
plt.title('Dilatación (Diamond)')
plt.axis('off')
plt.subplot(4, 5, 8)
plt.imshow(dilatacion_ball, cmap='gray')
plt.title('Dilatación (Ball)')
plt.axis('off')
plt.subplot(4, 5, 9)
plt.imshow(dilatacion_rectangle, cmap='gray')
plt.title('Dilatación (Rectangle)')
plt.axis('off')
plt.subplot(4, 5, 10)
plt.imshow(apertura_disk, cmap='gray')
plt.title('Apertura (Disk)')
plt.axis('off')
plt.subplot(4, 5, 11)
plt.imshow(apertura_diamond, cmap='gray')
plt.title('Apertura (Diamond)')
plt.axis('off')
plt.subplot(4, 5, 12)
plt.imshow(apertura_ball, cmap='gray')
plt.title('Apertura (Ball)')
plt.axis('off')
plt.subplot(4, 5, 13)
plt.imshow(apertura_rectangle, cmap='gray')
plt.title('Apertura (Rectangle)')
plt.axis('off')
plt.subplot(4, 5, 14)
plt.imshow(cierre_disk, cmap='gray')
plt.title('Cierre (Disk)')
plt.axis('off')
plt.subplot(4, 5, 15)
plt.imshow(cierre_diamond, cmap='gray')
plt.title('Cierre (Diamond)')
plt.axis('off')
plt.subplot(4, 5, 16)
plt.imshow(cierre_ball, cmap='gray')
plt.title('Cierre (Ball)')
plt.axis('off')
plt.subplot(4, 5, 17)
plt.imshow(cierre_rectangle, cmap='gray')
plt.title('Cierre (Rectangle)')
plt.axis('off')
plt.tight_layout()
plt.show()
Obtenemos resultados muy parecidos. Disk (Disco): Es una opción adecuada para suavizar y eliminar pequeños detalles en la imagen. Puede ayudar a cerrar pequeños huecos en objetos. Diamond (Diamante): Es útil para afinar y hacer que los objetos sean más puntiagudos. Puede ser eficaz para realzar bordes finos. Ball (Esfera): Funciona bien para suavizar y hacer crecer objetos en todas las direcciones. Útil para rellenar huecos en objetos grandes. Rectangle (Rectángulo): Es versátil y puede adaptarse a diferentes formas de objetos. Útil para cambios direccionales en la morfología.
Haciendo uso de las funcionalidades cargadas al principio, se pide hacer una función que:
Por último, ¿qué se podría hacer para asegurar que no se tienen en cuenta posibles errores en la umbralización como pequeños puntos o posible ruido que haya llegado hasta este punto?
# Función para contar el número de círculos
def count_circles(imagen_binaria):
# Haz cosas
num_circulos = 'pongo esto solo para que veais cómo sería la estructura'
return num_circulos
#Primero pasaremos nuestra imagen a binaria
import cv2
# Cargar la imagen en escala de grises (o convertirla si es una imagen en color)
imagen = cv2.imread('Pintura_Puntos.jpg', cv2.IMREAD_GRAYSCALE)
# Establecer un umbral (valor entre 0 y 255)
umbral = 128 # Puedes ajustar este valor según tus necesidades
# Aplicar la umbralización binaria
_, imagen_binaria = cv2.threshold(imagen, umbral, 255, cv2.THRESH_BINARY)
# Guardar la imagen binaria
cv2.imwrite('imagen_binaria.jpg', imagen_binaria)
# Mostrar la imagen binaria (opcional)
cv2.imshow('Imagen Binaria', imagen_binaria)
cv2.waitKey(0)
cv2.destroyAllWindows()
#con la imagen binaria ya guardada comprobaremos si es binaria o no
import numpy as np
def es_binaria(imagen_binaria):
# Verificar si la imagen es binaria
unique_values = np.unique(imagen_binaria)
if len(unique_values) == 2 and 0 in unique_values and 255 in unique_values:
return True
else:
return False
# Ejemplo de uso:
imagen_binaria = cv2.imread('imagen_binaria.jpg', cv2.IMREAD_GRAYSCALE)
if es_binaria(imagen_binaria):
print("La imagen es binaria.")
else:
print("La imagen no es binaria.")
La imagen no es binaria.
pip install --upgrade numpy scikit-image opencv-python
Requirement already up-to-date: numpy in c:\users\rodri\anaconda3\lib\site-packages (1.24.4) Requirement already up-to-date: scikit-image in c:\users\rodri\anaconda3\lib\site-packages (0.21.0) Requirement already up-to-date: opencv-python in c:\users\rodri\anaconda3\lib\site-packages (4.8.1.78) Requirement already satisfied, skipping upgrade: imageio>=2.27 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (2.31.5) Requirement already satisfied, skipping upgrade: tifffile>=2022.8.12 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (2023.7.10) Requirement already satisfied, skipping upgrade: packaging>=21 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (23.2) Requirement already satisfied, skipping upgrade: PyWavelets>=1.1.1 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (1.1.1) Requirement already satisfied, skipping upgrade: networkx>=2.8 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (3.1) Requirement already satisfied, skipping upgrade: pillow>=9.0.1 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (10.0.1) Requirement already satisfied, skipping upgrade: scipy>=1.8 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (1.10.1) Requirement already satisfied, skipping upgrade: lazy_loader>=0.2 in c:\users\rodri\anaconda3\lib\site-packages (from scikit-image) (0.3) Note: you may need to restart the kernel to use updated packages.
Primero realizaremos el método niblack y guardamos su imagen resultante para luego poder hacer el conteo de puntos de esa imagen.
from skimage import io, color, filters
import matplotlib.pyplot as plt
# Carga la imagen
imagen = io.imread('Pintura_Puntos.jpg')
# Convierte la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Aplica umbralización con threshold_niblack
umbralizado_niblack = filters.threshold_niblack(imagen_gris, window_size=15, k=0.4)
# Aplica el umbral
imagen_umbralizada_niblack = imagen_gris > umbralizado_niblack
# Guarda la imagen umbralizada en un archivo
io.imsave('imagen_umbralizada_niblack.jpg', imagen_umbralizada_niblack)
# Muestra la imagen umbralizada con threshold_niblack
plt.imshow(imagen_umbralizada_niblack, cmap='gray')
plt.axis('off')
plt.show()
<ipython-input-32-f0be187371e0>:17: UserWarning: imagen_umbralizada_niblack.jpg is a boolean image: setting True to 255 and False to 0. To silence this warning, please convert the image using img_as_ubyte.
io.imsave('imagen_umbralizada_niblack.jpg', imagen_umbralizada_niblack)
from skimage import io, color
# Carga la imagen binaria
imagen_binaria = io.imread('imagen_umbralizada_niblack.jpg', as_gray=True)
# Encontrar los puntos en la imagen umbralizada utilizando el detector FAST
puntos = feature.corner_peaks(feature.corner_fast(imagen_binaria), min_distance=5)
# Contar el número de puntos
num_puntos = len(puntos)
print(f"Número de puntos encontrados: {num_puntos}")
Número de puntos encontrados: 7727
Aumentar k aumentará el umbral, requiriendo valores de intensidad más altos para considerar un píxel como objeto, lo que podría reducir la detección de puntos. Disminuir k disminuirá el umbral, permitiendo detectar más puntos. La elección de k depende de la imagen y de lo que quieras detectar.
Esta sección no es obligatoria pero la pongo para aquellos que quieran saber "¿y ahora qué se haría?".
Lo que hemos hecho hasta ahora es:
Es decir, tenemos varios parámetros y tenemos una función que nos dice cuál es el número de puntos dada una imagen. Variando dichos parámetros, variará también el número de puntos, pero no parece haber una relación directa.
También no hay que olvidar que desconocemos el número de puntos (nunca se ha dicho, aunque siempre puedes contarlos), por lo que no podemos seguir un proceso de aprendizaje supervisado (tipo descenso del gradiente sobre los parámetros anteriores para encontrar el mejor resultado).
Pero lo que sí podemos hacer es iterar el valor de los parámetros para alcanzar un máximo de puntos (asumiendo que dicho máximo corresponderá con el mejor resultado). Esto suele hacerse cuando no sabemos exáctamente el resultado que esperamos.
En definitiva, ahora se buscaría realizar un proceso iterativo para encontrar el valor máximo del número de puntos. Para ello haría falta:
product del paquete itertools).Podría decirse que esa combinación de parámetros es la mejor.
import cv2
import numpy as np
from skimage import io, color, filters, feature
# Cargar la imagen desde tu ordenador
imagen = io.imread('Pintura_Puntos.jpg')
# Convertir la imagen a escala de grises
imagen_gris = color.rgb2gray(imagen)
# Aplicar el método Niblack para umbralizar la imagen
umbral_niblack = filters.threshold_niblack(imagen_gris, window_size=15, k=0.4)
# Aplicar el umbral a la imagen original
imagen_umbralizada = (imagen_gris > umbral_niblack).astype(np.uint8) * 255
# Encontrar los puntos en la imagen umbralizada utilizando el detector FAST
puntos = feature.corner_peaks(feature.corner_fast(imagen_umbralizada), min_distance=5)
# Contar el número de puntos
num_puntos = len(puntos)
print(f"Número de puntos encontrados: {num_puntos}")
Número de puntos encontrados: 7914
Al igual que en el anterior caso anterior, modificando los parametros window_size (siempre un número impar) y el valor de la k, variará el número de puntos de una forma u otra.
window_size (Tamaño de la ventana): Este parámetro determina el tamaño de la ventana utilizada para calcular el umbral local. Un valor mayor de window_size significa que se considera un área más grande alrededor de cada píxel para calcular el umbral local. Si aumentas window_size, es más probable que obtengas umbrales locales más estables y menos propensos al ruido, pero también puedes perder detalles finos. Por otro lado, si disminuyes window_size, los umbrales locales pueden ser más sensibles al ruido y a las pequeñas variaciones en la intensidad de la imagen, lo que podría resultar en más puntos detectados.
k: Este parámetro ajusta la sensibilidad del umbral local. Valores mayores de k aumentan el umbral local, lo que significa que se requerirá una intensidad más alta para considerar un píxel como parte de un objeto. Un valor de k más alto puede reducir la detección de puntos al requerir que los objetos sean más brillantes para ser detectados como puntos. Por otro lado, valores más bajos de k pueden permitir detectar más puntos al ser menos restrictivos en términos de umbral.